<?php
/**
 * Smarty模板Fastlib集成调用
 * 
 * $Id: fasttpl.php 128 2010-10-10 16:10:20Z Administrator $
 * $Revision: 128 $
 * $Author: Administrator $
 * $Date: 2010-10-11 00:10:20 +0800 (星期一, 11 十月 2010) $
 * 在fastlib中集成smarty，并扩展了smarty增加gettext支持
 * 增加的功能有wml输出，将非ANSI字符输出成HTML字符实体，模板目录控制，
 * 模板换肤，等功能。
 * 
 * @author glzaboy<glzaboy@163.com>
 * @package fastlib
 * @subpackage classes
 * @copyright 2005-2010 (c) glzaboy
 */
if (! defined ( '__FASTLIB__' )) {
	if (__DEBUG__) {
		die ( sprintf ( 'Forbidden 403 at file %s on line %d', __FILE__, __LINE__ ) );
	} else {
		die ( 'Forbidden 403' );
	}
}
setGetValue ( 'wml', '0', '/[^01]{1}/', 'session', true );
if (! class_exists ( "Smarty" )) {
	@fastinclude ( 'smarty/Smarty.class.php', 'include' );
}
if (! class_exists ( 'Smarty' )) {
	class Smarty {
		protected $halt = true;
	}
}
if (! defined ( '__FASTTPL__' )) {
	/**
	 * Fastlib内置模板目录
	 *
	 */
	define ( "__FASTTPL__", __FASTSYSTEM__ . "template/" );
}
/**
 * 初始化Smarty
 *
 * 当$encode为unicode时，将汉字输出成&#xxxx;的形式，此时模板的编码要用GBK
 * <code>
 * $t=new fasttpl();
 * $t->display("index.html");
 * </code>
 * 
 * @param String $encode 输出编码方式
 * @param String $delimiter 分隔符，注意是在原来{,}这的基础上增加
 * @param String $skin 皮肤
 * @param String $tpl_dir 模板目录，如果为relate时使用相对目录
 * @return String 模板句柄
 */
class fasttpl extends Smarty {
	private $outencode = null;
	/**
	 * 初始化Smarty
	 *
	 * 当$encode为unicode时，将汉字输出成&#xxxx;的形式，此时模板的编码要用GBK
	 * <code>
	 * $t=new fasttpl();
	 * $t->display("index.html");
	 * </code>
	 * 
	 * @param object $caller 调用者主要用于模块判断
	 * @param String $delimiter 分隔符，注意是在原来{,}这的基础上增加
	 * @param String $skin 皮肤主要用于预览
	 * @return String 模板句柄
	 */
	public function __construct($caller = null, $delimiter = '', $skin = null) {
		if (isset ( $this->halt ) && $this->halt) {
			return 'Smarty not exists!';
		}
		global $flibcfg;
		if ($flibcfg ['charset']) {
			$this->outencode = $flibcfg ['charset'];
		}
		setGetValue ( 'skin', 'default', '/[^a-zA-Z0-9]+/', 'session', true );
		if (is_string ( $skin ) && $skin !== null) {
			$_SESSION ['skin'] = $skin;
		} else {
			$skin = $_SESSION ['skin'];
		}
		$this->left_delimiter = '{' . $delimiter;
		$this->right_delimiter = $delimiter . '}';
		array_push ( $this->plugins_dir, __FASTSYSTEM__ . 'smarty_plug' ); //加入PLUG目录
		$this->template_dir = './view/' . $skin . '/templates';
		touchdir ( '.temp' );
		touchdir ( '.temp/tpl' );
		touchdir ( '.temp/tpl/' . $skin );
		$this->compile_dir = '.temp/tpl/' . $skin;
		$this->register_function ( 'gettext', array ($this, 'smarty_translate' ) );
		$this->register_function ( 'ngettext', array ($this, 'smarty_translate_ngettext' ) );
		$this->register_prefilter ( create_function ( '$tplString', 'return preg_replace_callback("/\{{$delimiter}[^\{$delimiter}]+{$delimiter}\}/", array(fasttpl,gettextCompiler), $tplString);' ) );
		global $sitecfg, $__CSS, $__RSS, $__JS, $__FETCH, $__INCLUDE;
		$this->assign_by_ref ( "site", $sitecfg );
		$this->assign_by_ref ( "CSS", $__CSS );
		$this->assign_by_ref ( "RSS", $__RSS );
		$this->assign_by_ref ( "JS", $__JS );
		$this->assign_by_ref ( "FETCH", $__FETCH ); //包含不解析
		$this->assign_by_ref ( "INCLUDE", $__INCLUDE ); //包含解析
		if (defined ( '__FASTMVC_ROOT__' ) && $caller !== null) {
			$this->assign_by_ref ( "caller", $caller );
		}
		return $this;
	}
	/**
	 * 多语言标签解析
	 * 
	 * 支持复数形式
	 * 不直接调用
	 *
	 * @param array $match preg_replace_callback 正则结果
	 * @return string 编译后的标签
	 */
	public function gettextCompiler($gettextArr) {
		$match = null;
		if (! preg_match ( '/__[n]?\(([^\)]+)\)[;]?/', $gettextArr [0], $match )) {
			unset ( $match );
			return $gettextArr [0];
		}
		if (substr ( trim ( $match [0] ), 0, 3 ) === '__n') { //ngettext
			$gettext_type = 'ngettext';
		} else {
			$gettext_type = 'gettext';
		}
		$match [1] = trim ( $match [1] );
		$len = strlen ( $match [1] );
		if ($len) {
			if ($gettext_type === 'ngettext') { //ngettext
				$delimiter = $match [1] [0];
				$inarg = false;
				$gettextargs = array ();
				$delimiterstart = 1;
				for($i = 0; $i < $len; ++ $i) {
					if ($i == 0) {
						$delimiter = $match [1] [$i];
						$inarg = true;
						continue;
					}
					if ($match [1] [$i] === ' ') {
						continue;
					}
					if ($match [1] [$i] === $delimiter) {
						if ($inarg) {
							array_push ( $gettextargs, substr ( $match [1], $delimiterstart - 1, $i - $delimiterstart + 2 ) );
							if (count ( $gettextargs ) == 2) {
								$argument = substr ( substr ( $match [1], $i + 1 ), strpos ( substr ( $match [1], $i + 1 ), ',' ) + 1 );
							}
							$inarg = false;
						} else {
							$delimiterstart = $i + 1;
							$delimiter = $match [1] [$i];
							$inarg = true;
						}
						continue;
					}
				}
			} else {
				$delimiter = $match [1] [0];
				for($i = 1; $i < $len; ++ $i) {
					if ($match [1] [$i] == $delimiter) {
						$gettextargs = substr ( $match [1], 0, $i + 1 );
						$argument = substr ( $match [1], $i + 2 );
						break;
					}
				}
			}
			$gettext = '';
			if ($argument) {
				$args = explode ( ',', $argument );
				foreach ( $args as $k => $v ) {
					$args [$k] = 'ARG' . $k . '=' . $v;
				}
				$gettext = implode ( ' ', $args );
			}
			if ('ngettext' == $gettext_type) {
				$gettext = 'ngettext single=' . $gettextargs [0] . ' plural=' . $gettextargs [1] . ' ' . $gettext;
			} else {
				$gettext = 'gettext text=' . $gettextargs . ' ' . $gettext;
			}
		} else {
			$gettext = 'gettext str=';
		}
		$return = preg_replace ( '/__[n]?\(([^\)]+)\)[;]?/', $gettext, $gettextArr [0] );
		unset ( $match, $gettextargs, $gettext_type );
		return $return;
	}
	/**
	 * 多语言模板翻译
	 *
	 * 不直接调用
	 *
	 * @param array $args smarty传过来的翻译字串
	 * @return string 翻译过后的字串
	 */
	public function smarty_translate($args) {
		array_unshift ( $args, __ ( array_shift ( $args ) ) );
		return call_user_func_array ( 'sprintf', $args );
	}
	/**
	 * 多语言模板复数形式翻译
	 *
	 * @param array $args smarty传过来的翻译字串
	 * @return string 翻译过后的字串
	 */
	public function smarty_translate_ngettext($args) {
		array_unshift ( $args, __n ( array_shift ( $args ), array_shift ( $args ), array_shift ( $args ) ) );
		return call_user_func_array ( 'sprintf', $args );
	}
	/**
	 * executes & displays the template results
	 *
	 * 是否压缩传输默认是开启的，最终还是取决于浏览器和服务器
	 * 
	 * @param string $resource_name
	 * @param bool $gzip 是否压缩传输
	 */
	public function display($resource_name) {
		global $flibcfg;
		if (isset ( $this->halt ) && $this->halt) {
			echo 'Smarty not exists!';
			return 'Smarty not exists!';
		}
		$output = ob_get_contents () . $this->fetch ( $resource_name );
		if ($flibcfg ['unicode']) {
			$output = charTounicode ( $output );
		}
		ob_clean ();
		if (isset ( $flibcfg ['gzip'] ) && $flibcfg ['gzip'] && strstr ( $_SERVER ['HTTP_ACCEPT_ENCODING'], 'gzip' ) && extension_loaded ( 'zlib' )) {
			header ( 'Content-Encoding:gzip' );
			header ( 'Vary: Accept-Encoding' );
			echo gzencode ( $output, 9 );
		} elseif (isset ( $flibcfg ['gzip'] ) && $flibcfg ['gzip'] && strstr ( $_SERVER ['HTTP_ACCEPT_ENCODING'], 'deflate' ) && extension_loaded ( 'zlib' )) {
			header ( 'Content-Encoding: deflate' );
			header ( 'Vary: Accept-Encoding' );
			echo gzdeflate ( $output, 9 );
		} else {
			echo $output;
		}
		return true;
	}
	/**
	 * executes & returns or displays the template results
	 *
	 * @param string $resource_name
	 * @param string $cache_id
	 * @param string $compile_id
	 * @param boolean $display
	 */
	public function fetch($resource_name, $cache_id = null, $compile_id = null) {
		if ($this->template_exists ( $resource_name )) {
			return parent::fetch ( $resource_name, $cache_id, $compile_id, false );
		}
		$restore = new stdClass ();
		$restore->template_dir = $this->template_dir;
		$restore->compile_dir = $this->compile_dir;
		$this->template_dir = './view/default/templates';
		touchdir ( '.temp' );
		touchdir ( '.temp/tpl' );
		touchdir ( '.temp/tpl/default' );
		$this->compile_dir = '.temp/tpl/default';
		if ($this->template_exists ( $resource_name )) {
			$returnValue = parent::fetch ( $resource_name, $cache_id, $compile_id, false );
		} else {
			if (__DEBUG__) {
				$returnValue = 'Template resource not exists!';
				$returnValue .= 'Template <b>' . $resource_name . '</b> resource not exists!<br />';
			} else {
				$returnValue = 'Server Error!';
			}
		}
		$this->template_dir = $restore->template_dir;
		$this->compile_dir = $restore->compile_dir;
		unset ( $restore );
		return $returnValue;
	}
	/**
	 * 返回本模块信息
	 *
	 * @return string
	 */
	public function printInfo() {
		$version = '$Revision: 128 $';
		$moudle = __CLASS__;
		$return = '';
		$return .= startTable ();
		$return .= printRow ( array (array ('class' => 'h', 'content' => '<center>' . $moudle . '</center>', 'colspan' => '2' ) ) );
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'author' ), array ('class' => 'v', 'content' => 'glzaboy' ) ) );
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'version' ), array ('class' => 'v', 'content' => $version ) ) );
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'html DTD vsersion support' ), array ('class' => 'v', 'content' => '2(2.0),3.2(3.2 Final),4.0f(4.0 frameset),4.0s(4.0 strict),4.0t(4.0 Transitional),4.01f(4.01 frameset),4.01s(4.01 strict),4.01t(4.01 Transitional),x1.0f(XHTML 1.0 Frameset),x1.0s(XHTML 1.0 Strict),x1.0t(XHTML 1.0 Transitional),x1.1(XHTML 1.1),x1.1ml(XHTML 1.1 plus MathML 2.0 plus SVG 1.1),x1.0mp(XHTML Mobile 1.0)' ) ) );
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'default Dtd' ), array ('class' => 'v', 'content' => htmlspecialchars ( '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"><html xmlns="http://www.w3.org/1999/xhtml">' ) ) ) );
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'zlib' ), array ('class' => 'v', 'content' => htmlspecialchars ( 'gzip,deflate' ) ) ) );
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'i18n support' ), array ('class' => 'v', 'content' => 'yes' ) ) );
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'i18n tags' ), array ('class' => 'v', 'content' => '__' ) ) );
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'i18n Plural tags' ), array ('class' => 'v', 'content' => '__n' ) ) );
		$smarty = new Smarty ();
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'Smarty Version' ), array ('class' => 'v', 'content' => ! isset ( $smarty->_version ) ? 'Smarty not install or not found.' : $smarty->_version ) ) );
		unset ( $smarty );
		$return .= printRow ( array (array ('class' => 'e', 'content' => 'Dependence' ), array ('class' => 'v', 'content' => 'smarty' ) ) );
		$return .= endTable ();
		return $return;
	}
}